﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using TireService.Data;
using DevExpress.Web.ASPxPivotGrid;
using DevExpress.Web.ASPxCallback;
using System.Configuration;
using System.Collections;
using System.Web.Security;
using System.IO;

namespace TireService.Web.View.ServiceView
{
    public partial class Planning : System.Web.UI.Page
    {
        #region Public Properties

        public int ServiceCarId
        {
            get
            {
                if (ViewState["_servicecarid"] != null)
                    return (int)ViewState["_servicecarid"];
                else
                    return 0;
            }
            set
            {
                ViewState["_servicecarid"] = value;
            }
        }

        public int ContainerId
        {
            get
            {
                if (ViewState["_containerid"] != null)
                    return (int)ViewState["_containerid"];
                else
                    return 0;
            }
            set
            {
                ViewState["_containerid"] = value;
            }
        }

        public int CustomerId
        {
            get
            {
                if (ViewState["_customerid"] != null)
                    return (int)ViewState["_customerid"];
                else
                    return 0;
            }
            set
            {
                ViewState["_customerid"] = value;
            }
        }

        #endregion

        protected void Page_Load(object sender, EventArgs e)
        {
            //if (!IsCallback && !IsPostBack)
            //    pgServiceCarCapacity.CellTemplate = new CellTemplate();

            TireServiceDB db = new TireServiceDB();

            if (Page.IsPostBack != true)
            {
                if (Request.QueryString["ContainerId"] != null) this.ContainerId = Convert.ToInt32(Request.QueryString["ContainerId"]);
                if (Request.QueryString["CustomerId"] != null) this.CustomerId = Convert.ToInt32(Request.QueryString["CustomerId"]);
                if (Request.QueryString["ServiceCarId"] != null) this.ServiceCarId = Convert.ToInt32(Request.QueryString["ServiceCarId"]);

                // Bind data to container dropdownlist
                ddlContainer.DataSource = db.GetContainers();
                ddlContainer.ValueField = "ContainerId";
                ddlContainer.TextField = "ContainerNumber";
                ddlContainer.DataBind();

                //ddlType.DataSource = db.ScheduleDetailTypes.ToList();
                //ddlType.ValueField = "ScheduleDetailTypeId";
                //ddlType.TextField = "Description";
                //ddlType.DataBind();

                // Select first container if no one preselected
                if (this.ContainerId == 0) ddlContainer.SelectedIndex = 1;

                // Test parameters
                txtSelectedStartDate.Date = DateTime.Now; //Convert.ToDateTime("20-02-2011");
                txtSelectedEndDate.Date = txtSelectedStartDate.Date.AddMonths(3);
            }

            // Show/hide reset button for selected car filter
            btnClearSelectedCarFilter.Visible = (Session["SelectedCars"] != null);

            // Show cars that need service
            BindCarNeedServiceData(db, Convert.ToInt32(ddlContainer.Value));

            // Show capacity on servicecars
            BindServiceCarCapacityData(db, db.GetContainerServiceCars(Convert.ToInt32(ddlContainer.Value)), txtSelectedStartDate.Date, txtSelectedEndDate.Date);

            // Handles modal popup with capacity gridview
            string columnIndexValue = ColumnIndex.Value, rowIndexValue = RowIndex.Value;
            if (!string.IsNullOrEmpty(columnIndexValue) && !string.IsNullOrEmpty(rowIndexValue))
                BindGridView(columnIndexValue, rowIndexValue);
            pgServiceCarCapacity.ClientSideEvents.CellClick = GetJSCellClickHandler();
            pcServiceCarCapacity.ClientSideEvents.Closing = GetJSPopupClosingHandler();
        }

        /// <summary>
        /// Function that bind data to gridview with list of cars that need service/tirechange
        /// </summary>
        /// <param name="db">Connection to database</param>
        /// <param name="ContainerId">Id on container where customers tires are placed</param>
        protected void BindCarNeedServiceData(TireServiceDB db, int ContainerId)
        {
            // Check if a range of cars is selected for planning
            if (Session["SelectedCars"] != null)
            {
                int[] cars = (int[])Session["SelectedCars"];
                //gvCarNeedService.DataSource = db.CustomerCarTireSetDistanceContainerViews.Where(p => p.CarStatusId == (int)Shared.CarStatus.NeedsTireChanged & p.ContainerId == ContainerId & p.ContainerCargoRoomId != null).Where(d => cars.Contains(d.CarId)).OrderBy(p => p.Distance).ToList();
                gvCarNeedService.DataSource = db.CustomerCarTireSetDistanceContainerViews.Where(p => p.CarStatusId == Convert.ToInt32(ddlCarStatus.SelectedItem.Value) & p.ContainerId == ContainerId & p.ContainerCargoRoomId != null).Where(d => cars.Contains(d.CarId)).OrderBy(p => p.Distance).ToList();
            }
            else
            {
                //gvCarNeedService.DataSource = db.CustomerCarTireSetDistanceContainerViews.Where(p => p.CarStatusId == (int)Shared.CarStatus.NeedsTireChanged & p.ContainerId == ContainerId & p.ContainerCargoRoomId != null).OrderBy(p => p.Distance).ToList();
                gvCarNeedService.DataSource = db.CustomerCarTireSetDistanceContainerViews.Where(p => p.CarStatusId == Convert.ToInt32(ddlCarStatus.SelectedItem.Value) & p.ContainerId == ContainerId & p.ContainerCargoRoomId != null).OrderBy(p => p.Distance).ToList();
            }

            // Bind to gridview
            gvCarNeedService.DataBind();
        }

        /// <summary>
        /// Function that bind data to pivottable with service car capacity overview
        /// </summary>
        /// <param name="db">Connection to database</param>
        /// <param name="ServiceCarId">Array with services cars that should be included in capacity overview</param>
        /// <param name="Startdate">Startdate</param>
        /// <param name="Enddate">Enddate</param>
        protected void BindServiceCarCapacityData(TireServiceDB db, int[] ServiceCarId, DateTime Startdate, DateTime Enddate)
        {
            pgServiceCarCapacity.DataSource = db.ServiceCarScheduleDetailViews.Where(p => p.ScheduleDate <= Enddate & p.ScheduleDate >= Startdate).Where(d => ServiceCarId.Contains(d.ServiceCarId)).ToList();
            pgServiceCarCapacity.DataBind();
        }

        /// <summary>
        /// Gridview function that runs when databinding - Changes colors based on car on primary address
        /// </summary>
        protected void gvCarNeedService_HtmlRowPrepared(object sender, DevExpress.Web.ASPxGridView.ASPxGridViewTableRowEventArgs e)
        {
            if (e.RowType != DevExpress.Web.ASPxGridView.GridViewRowType.Data) return;

            // Get OnPrimaryAddressInPrimeTime value - based on that rows will be formated
            bool OnPrimaryAddressInPrimeTime = Convert.ToBoolean(gvCarNeedService.GetRowValuesByKeyValue(e.KeyValue, "OnPrimaryAddressInPrimeTime"));

            if (!OnPrimaryAddressInPrimeTime)
            {
                e.Row.BackColor = System.Drawing.Color.Bisque;
            }
        }

        /// <summary>
        /// Gridview function that runs when databinding - Changes colors based on customerconfirmed value
        /// </summary>
        protected void gvServiceCarCapacity_HtmlRowPrepared(object sender, DevExpress.Web.ASPxGridView.ASPxGridViewTableRowEventArgs e)
        {
            if (e.RowType != DevExpress.Web.ASPxGridView.GridViewRowType.Data) return;

            int servicetype = Convert.ToInt32(gvServiceCarCapacity.GetRowValuesByKeyValue(e.KeyValue, "ScheduleDetailTypeId"));

            if (servicetype == (int)Shared.ScheduleDetailType.Available)
            {
                e.Row.BackColor = System.Drawing.Color.White;
            }
            if (servicetype == (int)Shared.ScheduleDetailType.Blocked)
            {
                e.Row.BackColor = System.Drawing.Color.Red;
            }
            if (servicetype == (int)Shared.ScheduleDetailType.ServiceDrive)
            {
                e.Row.BackColor = System.Drawing.Color.Blue;
            }
            //if (servicetype == (int)Shared.ScheduleDetailType.TirePickUp)
            //{
            //    e.Row.BackColor = System.Drawing.Color.BlueViolet;
            //}
            //if (servicetype == (int)Shared.ScheduleDetailType.TireReturn)
            //{
            //    e.Row.BackColor = System.Drawing.Color.DarkRed;
            //}
            //if (servicetype == (int)Shared.ScheduleDetailType.TireChange)
            //{
            //    // Get customerconfirmed value - based on that rows will be formated
            //    bool CustomerConfirmed = Convert.ToBoolean(gvServiceCarCapacity.GetRowValuesByKeyValue(e.KeyValue, "CustomerConfirmed"));

            //    if (CustomerConfirmed)
            //    {
            //        e.Row.BackColor = System.Drawing.Color.DarkGreen;
            //    }
            //    else
            //    {

            //        e.Row.BackColor = System.Drawing.Color.Orange;
            //    }
            //}
        }

        /// <summary>
        /// Postback function that updates overview of capacity on specific service car and date
        /// </summary>
        protected void gvServiceCarCapacity_CustomCallback(object sender, DevExpress.Web.ASPxGridView.ASPxGridViewCustomCallbackEventArgs e)
        {
            string[] param = e.Parameters.Split('|');
            if (param[0] != "D") return;
            BindGridView(ColumnIndex.Value, RowIndex.Value);
            gvServiceCarCapacity.PageIndex = 0;
        }

        /// <summary>
        /// Postback function that updates gridview with cars that need service
        /// </summary>
        protected void gvCarNeedService_CustomCallback(object sender, DevExpress.Web.ASPxGridView.ASPxGridViewCustomCallbackEventArgs e)
        {
            TireServiceDB db = new TireServiceDB();

            // Show cars that need service
            BindCarNeedServiceData(db, Convert.ToInt32(ddlContainer.Value));
        }

        /// <summary>
        /// Postback function that updates overview of capacity on service cars
        /// </summary>
        protected void pgServiceCarCapacity_CustomCallback(object sender, DevExpress.Web.ASPxPivotGrid.PivotGridCustomCallbackEventArgs e)
        {
            TireServiceDB db = new TireServiceDB();

            // Show capacity on servicecars
            BindServiceCarCapacityData(db, db.GetContainerServiceCars(Convert.ToInt32(ddlContainer.Value)), txtSelectedStartDate.Date, txtSelectedEndDate.Date);
        }

        /// <summary>
        /// Function that binds data to gridview with view on capacity for selected servicecar and day
        /// </summary>
        /// <param name="columnIndex"></param>
        /// <param name="rowIndex"></param>
        protected void BindGridView(string columnIndex, string rowIndex)
        {
            gvServiceCarCapacity.DataSource = pgServiceCarCapacity.CreateDrillDownDataSource(Int32.Parse(columnIndex), Int32.Parse(rowIndex));
            gvServiceCarCapacity.DataBind();

            // Hide create timeslot if no timeslot exists already - need to use generate timeslots first
            pCreateNewTimeslot.Visible = (gvServiceCarCapacity.Columns.Count != 0);
        }

        protected string GetJSCellClickHandler()
        {
            return string.Format(@"function (s, e) {{
                var columnIndex = document.getElementById('{0}'),
                rowIndex = document.getElementById('{1}');
                columnIndex.value = e.ColumnIndex;
                rowIndex.value = e.RowIndex;
                GridView.SelectAllRowsOnPage(false);
                GridView.PerformCallback('D');
                ShowDrillDown();
                
            }}", ColumnIndex.ClientID, RowIndex.ClientID);
        }
        protected string GetJSPopupClosingHandler()
        {   
            return string.Format(@"function (s, e) {{
                var columnIndex = document.getElementById('{0}'),
                rowIndex = document.getElementById('{1}');
                columnIndex.value = '';
                rowIndex.value = ''; 
            }}", ColumnIndex.ClientID, RowIndex.ClientID);
        }

        /// <summary>
        /// Callback function that creates a single timeslot
        /// </summary>
        protected void callCreateSingleTimeslot_Callback(object sender, CallbackEventArgs e)
        {
            // Get values (servicecarid, scheduledate, duration, overlap)
            string[] param = e.Parameter.Split(',');

            // Save new timeslot          
            int servicecarid = Convert.ToInt32(param[0]);
            DateTime datetime = Convert.ToDateTime(param[1]);

            TireServiceDB db = new TireServiceDB();

            ServiceCarSchedule schedule = db.GetServiceCarSchedule(servicecarid, datetime);

            ServiceCarScheduleDetail lastscheduledetail = schedule.ServiceCarScheduleDetails.OrderBy(p => p.SequenceNumber).Last();

            ServiceCarScheduleDetail scheduledetail = new ServiceCarScheduleDetail();
            schedule.ServiceCarScheduleDetails.Add(scheduledetail);
            scheduledetail.ScheduleDetailTypeId = (int)Shared.ScheduleDetailType.Available; // Convert.ToInt32(ddlType.Value);
            scheduledetail.SequenceNumber = lastscheduledetail.SequenceNumber + 1;
            scheduledetail.StartTime = lastscheduledetail.EndTime - TimeSpan.FromMinutes(Convert.ToInt32(param[3]));
            scheduledetail.EndTime = scheduledetail.StartTime.Add(TimeSpan.FromMinutes(Convert.ToInt32(param[2])));

            db.AddServiceCarScheduleDetail(scheduledetail);

            db.SubmitChanges();
        }

        protected void btnClearSelectedCarFilter_Click(object sender, EventArgs e)
        {
            // Reset filter - stored in session
            Session["SelectedCars"] = null;

            TireServiceDB db = new TireServiceDB();
            
            // Rebind gridview with cars that need service
            BindCarNeedServiceData(db, Convert.ToInt32(ddlContainer.Value));

            // Hide reset filter button
            btnClearSelectedCarFilter.Visible = false;
        }

        /// <summary>
        /// Callback function that send email with request for approval
        /// </summary>
        protected void callBackRequestApprovalSelectedCustomers_Callback(object sender, CallbackEventArgs e)
        {
            // Get values
            string[] param = e.Parameter.Split(',');

            // Instantiate database
            TireServiceDB db = new TireServiceDB();

            for (int i = 0; i < param.Length; i++)
            {
                SendConfirmationEmailToSelected(db, Convert.ToInt32(param[i]));
            }
            
            // Commit changes to database
            db.SubmitChanges();

            // Dispose database connection
            db.Dispose();
        }

        /// <summary>
        /// Callback function that set selected servicedetails as approved
        /// </summary>
        protected void callBackSetSelectedAsApproved_Callback(object sender, CallbackEventArgs e)
        {
            // Send email to selected customers
            string[] param = e.Parameter.Split(',');

            // Instantiate database
            TireServiceDB db = new TireServiceDB();

            for (int i = 0; i < param.Length; i++)
            {
                CustomerConfirmed(db, Convert.ToInt32(param[i]), true);
            }
            
            // Commit changes to database
            db.SubmitChanges();

            // Rebind gridview
            BindGridView(ColumnIndex.Value, RowIndex.Value);

            // Dispose database connection
            db.Dispose();
        }

        /// <summary>
        /// Callback function that remove approval from selected servicedetails
        /// </summary>
        protected void callBackRemoveApprovalOnSelected_Callback(object sender, CallbackEventArgs e)
        {
            // Send email to selected customers
            string[] param = e.Parameter.Split(',');

            // Instantiate database
            TireServiceDB db = new TireServiceDB();

            for (int i = 0; i < param.Length; i++)
            {
                CustomerConfirmed(db, Convert.ToInt32(param[i]), false);
            }
            
            // Commit changes to database
            db.SubmitChanges();

            // Rebind gridview
            BindGridView(ColumnIndex.Value, RowIndex.Value);

            // Dispose database connection
            db.Dispose();
        }

        /// <summary>
        /// Callback function that add block on selected servicedetails
        /// </summary>
        protected void callBackAddBlockOnSelected_Callback(object sender, CallbackEventArgs e)
        {
            // Get selected servicedetails
            string[] param = e.Parameter.Split(',');

            // Instantiate database
            TireServiceDB db = new TireServiceDB();

            for (int i = 0; i < param.Length; i++)
            {
                ServiceCarScheduleDetail detail = db.GetServiceCarScheduleDetail(Convert.ToInt32(param[i]));

                if (detail.ScheduleDetailTypeId == (int)Shared.ScheduleDetailType.Available)
                {
                    detail.ScheduleDetailTypeId = (int)Shared.ScheduleDetailType.Blocked;
                    detail.Note = "Tidsrum blokeret";
                }                
            }

            // Commit changes to database
            db.SubmitChanges();

            // Rebind gridview
            BindGridView(ColumnIndex.Value, RowIndex.Value);

            // Dispose database connection
            db.Dispose();
        }

        /// <summary>
        /// Callback function that saves manual edit of servicedetail
        /// </summary>
        protected void callBackSaveServiceDetail_Callback(object sender, CallbackEventArgs e)
        {
            string[] param = e.Parameter.Split(',');

            // Instantiate database
            TireServiceDB db = new TireServiceDB();

            // Fecth detail to update
            ServiceCarScheduleDetail detail = db.GetServiceCarScheduleDetail(Convert.ToInt32(param[0]));

            if (detail != null)
            {
                //if (detail.ScheduleDetailTypeId == (int)Shared.ScheduleDetailType.ServiceDrive | detail.ScheduleDetailTypeId == (int)Shared.ScheduleDetailType.TireChange)
                    
                if (detail.ScheduleDetailTypeId == (int)Shared.ScheduleDetailType.ServiceDrive)
                {
                    detail.ToAddress = param[1].ToString();
                    detail.ToZipCode = param[2].ToString();
                    detail.ToCity = param[3].ToString();
                    detail.StartTime = TimeSpan.Parse(param[4]);
                    detail.EndTime = TimeSpan.Parse(param[5]);
                    detail.ToLatitude = double.Parse(param[6]);
                    detail.ToLongitude = double.Parse(param[7]);    
                }
            }

            // Commit changes to database
            db.SubmitChanges();

            // Rebind gridview
            BindGridView(ColumnIndex.Value, RowIndex.Value);

            // Dispose database connection
            db.Dispose();
        }

        /// <summary>
        /// Callback function that creates url to google directions for serviceschedule
        /// </summary>
        protected void callGenerateURLGoogleDirection_Callback(object sender, CallbackEventArgs e)
        {
            // Get values (servicecarid, scheduledate)
            string[] param = e.Parameter.Split(',');

            // Input values for fetching
            int servicecarid = Convert.ToInt32(param[0]);
            DateTime datetime = Convert.ToDateTime(param[1]);

            // Initiate datalayer
            TireServiceDB db = new TireServiceDB();

            // Fetch servicecarschedule
            ServiceCarSchedule schedule = db.GetServiceCarSchedule(servicecarid, datetime);
            if (schedule != null)
            {
                List<ServiceCarScheduleDetail> details = schedule.ServiceCarScheduleDetails.ToList();

                string GoogleUrl = "http://maps.google.com/maps?saddr=";
                string AddressPlaceholder = "";
                int i = 1;

                // Generate URL
                foreach (ServiceCarScheduleDetail detail in details)
                {
                    if (detail.ScheduleDetailTypeId != (int)Shared.ScheduleDetailType.Blocked & detail.ScheduleDetailTypeId != (int)Shared.ScheduleDetailType.Available)
                    {
                        StringWriter writer = new StringWriter();
                        AddressPlaceholder = detail.ToAddress + ", " + detail.ToZipCode + ", " + detail.ToCity + ", " + db.GetCountryName(Convert.ToInt32(detail.ToCountryId));
                        Server.UrlEncode(AddressPlaceholder, writer);
                        if (i == 1)
                        {
                            GoogleUrl += writer.ToString();
                        }
                        else
                        {
                            GoogleUrl += "+to:" + writer.ToString();
                        }
                        i += 1;

                        writer.Dispose();
                    }
                }

                GoogleUrl += "&hl=da&ie=UTF8&mra=ls&t=m&z=9";

                e.Result = GoogleUrl;
            }
            else
            {
                e.Result = "";
            }
        }

        /// <summary>
        /// Callback function that saves note on servicedetail
        /// </summary>
        protected void callBackSaveNote_Callback(object sender, CallbackEventArgs e)
        {
            // Send email to selected customers
            string[] param = e.Parameter.Split(',');

            // Instantiate database
            TireServiceDB db = new TireServiceDB();

            // Fecth detail to update
            ServiceCarScheduleDetail detail = db.GetServiceCarScheduleDetail(Convert.ToInt32(param[0]));

            if (detail != null)
            {
                detail.Note = param[1].ToString();    
            }
            
            // Commit changes to database
            db.SubmitChanges();

            // Rebind gridview
            BindGridView(ColumnIndex.Value, RowIndex.Value);

            // Dispose database connection
            db.Dispose();
        }

        protected void cbPanelCreateTimeslots_OnCallback(object sender, DevExpress.Web.ASPxClasses.CallbackEventArgsBase e)
        {
            // Generate possible dates
            DateTime start = Convert.ToDateTime(txtSelectedStartDate.Text);
            DateTime end = Convert.ToDateTime(txtSelectedEndDate.Text);

            List<DevExpress.Web.ASPxEditors.ListEditItem> dates = new List<DevExpress.Web.ASPxEditors.ListEditItem>();

            if (start < end)
            {
                DateTime interval = start;

                while (interval < end)
                {
                    DevExpress.Web.ASPxEditors.ListEditItem newdate = new DevExpress.Web.ASPxEditors.ListEditItem();
                    newdate.Text = interval.ToShortDateString();
                    newdate.Value = interval.ToShortDateString();
                    dates.Add(newdate);

                    interval = interval.AddDays(1);
                }
            }
            else
            {
                DevExpress.Web.ASPxEditors.ListEditItem defaultdate = new DevExpress.Web.ASPxEditors.ListEditItem();
                defaultdate.Text = start.ToShortDateString();
                defaultdate.Value = start.ToShortDateString();
                dates.Add(defaultdate);
            }

            ddlDate.DataSource = dates;
            ddlDate.DataBind();

            TireServiceDB db = new TireServiceDB();

            // Generate possible servicecars
            int[] servicecarids = db.GetContainerServiceCars(Convert.ToInt32(ddlContainer.Value));

            rbtnServiceCar.DataSource = db.ServiceCars.Where(d => servicecarids.Contains(d.ServiceCarId)).ToList();
            rbtnServiceCar.TextField = "ServiceCarId";
            rbtnServiceCar.ValueField = "ServiceCarId";
            rbtnServiceCar.DataBind();
        }

        /// <summary>
        /// Callback function that generates and creates empty timeslots in serviceschedule
        /// </summary>
        protected void callBackCreateTimeslots_Callback(object sender, CallbackEventArgs e)
        {
            // Get values (servicecarid, scheduledate, starttime, endtime, slotinterval, overlap, selectwholeperiod, startdate, enddate
            string[] param = e.Parameter.Split(',');

            TireServiceDB db = new TireServiceDB();

            // TODO: Create code for creating timeslots for whole period selected

            int servicecarid = Convert.ToInt32(param[0]);
            DateTime datetime = Convert.ToDateTime(param[1]);

            if (!db.ScheduleDateExists(servicecarid, datetime))
            {
                ServiceCarSchedule schedule = new ServiceCarSchedule();

                schedule.ServiceCarId = servicecarid;
                schedule.ScheduleDate = datetime;

                db.AddServiceCarSchedule(schedule);

                int startnumber = 1;
                TimeSpan starttime = TimeSpan.Parse(param[2]);
                TimeSpan latestendtime = TimeSpan.Parse(param[3]);

                while (starttime.Add(TimeSpan.FromMinutes(Convert.ToInt32(param[4]))) < latestendtime)
                {
                    ServiceCarScheduleDetail scheduledetail = new ServiceCarScheduleDetail();
                    schedule.ServiceCarScheduleDetails.Add(scheduledetail);
                    scheduledetail.ScheduleDetailTypeId = (int)Shared.ScheduleDetailType.Available;
                    scheduledetail.SequenceNumber = startnumber;
                    scheduledetail.StartTime = starttime;
                    scheduledetail.EndTime = scheduledetail.StartTime.Add(TimeSpan.FromMinutes(Convert.ToInt32(param[4])));

                    // Calculate new starttime for next scheduledetail
                    starttime = scheduledetail.EndTime - TimeSpan.FromMinutes(Convert.ToInt32(param[5]));
                    startnumber += 1;

                    db.AddServiceCarScheduleDetail(scheduledetail);
                }

                db.SubmitChanges();
            }
            else
            {
                // Date already planned with timeslots
                throw new ArgumentException("Tider allerede oprettet denne dag. Venligst vælg dag og tilføj manuelt.");
            }
        }

        /// <summary>
        /// Callback function that bookes selected car into selected servicedetail
        /// </summary>
        protected void callBookService_Callback(object sender, CallbackEventArgs e)
        {
            // Get input parameters from client (carid, servicescheduledetailid)
            string[] param = e.Parameter.Split(',');

            int CarId = Convert.ToInt32(param[0]);
            int ServiceCarScheduleDetailId = Convert.ToInt32(param[1]);
            bool OtherAddress = Convert.ToBoolean(param[2]);

            TireServiceDB db = new TireServiceDB();

            // Check if user has selected car from carneedservice gridview
            Car car = db.GetCar(CarId);
            if (car == null)
            {
                throw new ArgumentException("Ingen bil valgt - venligst vælg bil først.");
            }

            //// If car has status of needing tires picked up - action can not be performed, must be done through dropdown actions
            //if (car.CarStatusId == (int)Shared.CarStatus.NeedsTirePickedUp)
            //{
            //    throw new ArgumentException("Brug dropdownlist handling for at planlægge afhentning af hjul.");
            //}

            // Get servicecarscheduledetail record
            ServiceCarScheduleDetail scheduledetail = db.GetServiceCarScheduleDetail(ServiceCarScheduleDetailId);
            if (scheduledetail == null)
            {
                throw new ArgumentException("Valgt tidsrum eksistere ikke - venligst prøv igen.");
            }

            // Check if timeslot is "available"
            if (scheduledetail.ScheduleDetailTypeId != (int)Shared.ScheduleDetailType.Available)
            {
                throw new ArgumentException("Valgt tidsrum ikke ledigt - venligst vælg et andet.");
            }
            
            // Fetch Customer
            Customer customer = db.GetCustomer(car.CustomerId);

            // Insert car on timeslot and update scheduledetail
            //scheduledetail.ScheduleDetailTypeId = (int)Shared.ScheduleDetailType.TireChange;
            int carstatustype = Convert.ToInt32(ddlCarStatus.SelectedItem.Value);
            scheduledetail.CarId = car.CarId;
            if (carstatustype == (int)Shared.CarStatus.NeedsTireChanged)
            {
                scheduledetail.FromTireSetId = car.TireSets.SingleOrDefault(p => p.TireSetStatusId == (int)Shared.TireSetStatus.MountedOnCar).TireSetId;
                scheduledetail.ToTireSetId = car.TireSets.SingleOrDefault(p => p.TireSetStatusId == (int)Shared.TireSetStatus.OnStock).TireSetId;
                scheduledetail.CustomerConfirmed = cbCustomerConfirmed.Checked;
            }
            if (cbSecondaryAddress.Checked)
            {
                scheduledetail.ToAddress = customer.SecondaryAddress;
                scheduledetail.ToZipCode = customer.SecondaryZipCode;
                scheduledetail.ToCity = customer.SecondaryCity;
                scheduledetail.ToCountryId = customer.CountryId;
                scheduledetail.ToLatitude = customer.LatitudeSecondary;
                scheduledetail.ToLongitude = customer.LongitudeSecondary;
            }
            else
            {
                if (OtherAddress)
                {
                    scheduledetail.ToAddress = Convert.ToString(param[3]);
                    scheduledetail.ToZipCode = Convert.ToString(param[4]);
                    scheduledetail.ToCity = Convert.ToString(param[5]);
                    scheduledetail.ToCountryId = customer.CountryId;

                    // Calculate longitude and latitude for address
                    try
                    {
                        string primaryAddress = scheduledetail.ToAddress + ", " + scheduledetail.ToZipCode + " " + scheduledetail.ToCity + ", " + db.GetCountryName(customer.CountryId);
                        GoogleMaps googleMapAPI = new GoogleMaps(ConfigurationManager.AppSettings["MapKey"].ToString());
                        double[] LongLatPrimary = googleMapAPI.GetLongLatCoordinates(primaryAddress);
                        scheduledetail.ToLongitude = LongLatPrimary[0];
                        scheduledetail.ToLatitude = LongLatPrimary[1];
                    }
                    catch (Exception)
                    {
                        // Error calculating longitude and latitude - values will be set to null
                    }
                }
                else
                {
                    scheduledetail.ToAddress = customer.Address;
                    scheduledetail.ToZipCode = customer.ZipCode;
                    scheduledetail.ToCity = customer.City;
                    scheduledetail.ToCountryId = customer.CountryId;
                    scheduledetail.ToLatitude = customer.LatitudePrimary;
                    scheduledetail.ToLongitude = customer.LongitudePrimary;
                }
            }
            scheduledetail.Note = txtNote.Text;

            // Update car with new status
            if (carstatustype == (int)Shared.CarStatus.NeedsTireChanged)
            {
                //scheduledetail.ScheduleDetailTypeId = (int)Shared.ScheduleDetailType.TireChange;
                scheduledetail.ScheduleDetailTypeId = (int)Shared.ScheduleDetailType.ServiceDrive;
                car.CarStatusId = (int)Shared.CarStatus.PlannedTireChange;
            }
            //if (carstatustype == (int)Shared.CarStatus.NeedsTirePickedUp)
            //{
            //    scheduledetail.ScheduleDetailTypeId = (int)Shared.ScheduleDetailType.TirePickUp;
            //    car.CarStatusId = (int)Shared.CarStatus.PlannedTirePickUp;
            //}
            //if (carstatustype == (int)Shared.CarStatus.NeedsTireReturned)
            //{
            //    scheduledetail.ScheduleDetailTypeId = (int)Shared.ScheduleDetailType.TireReturn;
            //    car.CarStatusId = (int)Shared.CarStatus.PlannedTireReturn;
            //}

            // Submit changes to database
            db.SubmitChanges();

            // Dispose database connection
            db.Dispose();
        }

        /// <summary>
        /// Callback function that deletes bookes servicedetail
        /// </summary>
        protected void callDeleteBookedService_Callback(object sender, CallbackEventArgs e)
        {
            // Get input parameters from client (servicescheduledetailid)
            string[] param = e.Parameter.Split(',');

            int ServiceCarScheduleDetailId = Convert.ToInt32(param[0]);

            TireServiceDB db = new TireServiceDB();

            // Get servicecarscheduledetail record
            ServiceCarScheduleDetail scheduledetail = db.GetServiceCarScheduleDetail(ServiceCarScheduleDetailId);
            if (scheduledetail == null)
            {
                throw new ArgumentException("Valgt tidsrum eksistere ikke - venligst prøv igen.");
            }

            // Check if timeslot is booked or blocked
            //if (scheduledetail.ScheduleDetailTypeId != (int)Shared.ScheduleDetailType.TireChange | scheduledetail.ScheduleDetailTypeId != (int)Shared.ScheduleDetailType.Blocked | scheduledetail.ScheduleDetailTypeId != (int)Shared.ScheduleDetailType.ServiceDrive)
            if (scheduledetail.ScheduleDetailTypeId == (int)Shared.ScheduleDetailType.Available)
            {
                throw new ArgumentException("Valgt tidsrum er ikke booket - venligst vælg et andet.");
            }

            // Check if this is a booking related to a customer car
            if (scheduledetail.CarId != null)
            {
                // Fetch car and change status
                Car car = db.GetCar(Convert.ToInt32(scheduledetail.CarId));

                //if (scheduledetail.ScheduleDetailTypeId == (int)Shared.ScheduleDetailType.TireChange)
                if (scheduledetail.ScheduleDetailTypeId == (int)Shared.ScheduleDetailType.ServiceDrive)
                {
                    // Update car with new status
                    car.CarStatusId = (int)Shared.CarStatus.NeedsTireChanged;
                }

                //if (scheduledetail.ScheduleDetailTypeId == (int)Shared.ScheduleDetailType.TirePickUp)
                //{
                //    // Update car with new status
                //    car.CarStatusId = (int)Shared.CarStatus.NeedsTirePickedUp;
                //}

                //if (scheduledetail.ScheduleDetailTypeId == (int)Shared.ScheduleDetailType.TireReturn)
                //{
                //    // Update car with new status
                //    car.CarStatusId = (int)Shared.CarStatus.NeedsTireReturned;
                //}
            }

            // Update timeslot - reset to available status
            scheduledetail.ScheduleDetailTypeId = (int)Shared.ScheduleDetailType.Available;
            scheduledetail.CarId = null;
            scheduledetail.FromTireSetId = null;
            scheduledetail.ToTireSetId = null;

            scheduledetail.ToAddress = null;
            scheduledetail.ToZipCode = null;
            scheduledetail.ToCity = null;
            scheduledetail.ToCountryId = null;
            scheduledetail.ToLatitude = null;
            scheduledetail.ToLongitude = null;

            scheduledetail.Note = null;
            scheduledetail.CustomerConfirmed = false;

            // Commit changes to database
            db.SubmitChanges();

            // Dispose database connection
            db.Dispose();
        }

        /// <summary>
        /// Function that sends confirmation email to selected customer
        /// </summary>
        /// <param name="db">Connection to database</param>
        /// <param name="ServiceCarScheduleDetailId">Id on booking to send email for</param>
        private void SendConfirmationEmailToSelected(TireServiceDB db, int ServiceCarScheduleDetailId)
        {
            // Fecth detail
            ServiceCarScheduleDetailView detail = db.ServiceCarScheduleDetailViews.SingleOrDefault(p => p.ServiceCarScheduleDetailId == ServiceCarScheduleDetailId);

            if (detail != null)
            {
                //if (detail.ScheduleDetailTypeId == (int)Shared.ScheduleDetailType.TireChange)
                if (detail.ScheduleDetailTypeId == (int)Shared.ScheduleDetailType.ServiceDrive)
                {
                    // Send email
                    SendConfirmationEmail(db, detail);    
                }
            }
        }

        /// <summary>
        /// Function that change confirmation on servicedetail for customer
        /// </summary>
        /// <param name="db">Connection to database</param>
        /// <param name="ServiceCarScheduleDetailId">Id on booking to set as customer confirmed/not confirmed</param>
        /// <param name="CustomerConfirmed">True/False value indicating if customer confirmed booking</param>
        private void CustomerConfirmed(TireServiceDB db, int ServiceCarScheduleDetailId, bool CustomerConfirmed)
        {
            // Fecth detail to update
            ServiceCarScheduleDetail detail = db.GetServiceCarScheduleDetail(ServiceCarScheduleDetailId);

            if (detail != null)
            {
                //if (detail.ScheduleDetailTypeId == (int)Shared.ScheduleDetailType.TireChange)
                if (detail.ScheduleDetailTypeId == (int)Shared.ScheduleDetailType.ServiceDrive)
                {
                    // Update detail
                    detail.CustomerConfirmed = CustomerConfirmed;        
                }
            }
        }

        /// <summary>
        /// Function that sends email to customer for a specific servicedetail
        /// </summary>
        /// <param name="db">Database connection</param>
        /// <param name="scheduledetail">Servicedetail</param>
        private void SendConfirmationEmail(TireServiceDB db, ServiceCarScheduleDetailView scheduledetail)
        {
            // Send email
            SendEmail(scheduledetail.Email, scheduledetail.Name, scheduledetail.TireTypeDescription, scheduledetail.ScheduleDate.ToShortDateString(), scheduledetail.StartTime.ToString() + "-" + scheduledetail.EndTime.ToString(), scheduledetail.ToAddress, scheduledetail.ToZipCode, scheduledetail.ToCity, scheduledetail.DetailGuid.ToString(), scheduledetail.CustomerId.ToString(), scheduledetail.SubscriptionId.ToString(), scheduledetail.CustomerSubscriptionId.ToString());

            // Update serviceschedule detail
            ServiceCarScheduleDetail updatescheduledetail = db.GetServiceCarScheduleDetail(scheduledetail.ServiceCarScheduleDetailId);
            updatescheduledetail.CustomerEmailSendCount = updatescheduledetail.CustomerEmailSendCount + 1;
        }

        /// <summary>
        /// Function that sends email to customer with planned service date, time and location and link for confirming or declining planned service
        /// </summary>
        /// <param name="CustomerEmail">Customer email that this email will be send to</param>
        /// <param name="CustomerName">Name on customer</param>
        /// <param name="TireSetType">Tiretype description (Winther/Summer)</param>
        /// <param name="ScheduleDate">Scheduledate for the service</param>
        /// <param name="TimeSpan">Timespan for the service</param>
        /// <param name="Address">Address for service</param>
        /// <param name="ZipCode">ZipCode for service</param>
        /// <param name="City">City for service</param>
        /// <param name="DetailGuid">Unik id for serviceschedule detail - used in email links</param>
        private void SendEmail(string CustomerEmail, string CustomerName, string TireSetType, string ScheduleDate, string TimeSpan, string Address, string ZipCode, string City, string DetailGuid, string CustomerId, string SubscriptionId, string CustomerSubscriptionId)
        {
            string Subject = "Booking af tid til hjulskift (Abonnement " + CustomerId + "-" + SubscriptionId + "-" + CustomerSubscriptionId + ")";
            string CustomerApprovalLink = ConfigurationManager.AppSettings["CustomerConfirmationURL"].ToString() + "?ApprovalId=" + DetailGuid;
            string CustomerDisapprovalLink = ConfigurationManager.AppSettings["CustomerConfirmationURL"].ToString() + "?ApprovalId=" + DetailGuid + "&Approve=false";
            bool test = Convert.ToBoolean(Convert.ToByte(ConfigurationManager.AppSettings["TestEnvironmentOn"]));
            Shared email = new Shared();

            Hashtable templateVars = new Hashtable();

            // Send email to customer
            templateVars.Add("CustomerName", CustomerName);
            templateVars.Add("TireSetType", TireSetType);
            templateVars.Add("ScheduleDate", ScheduleDate);
            templateVars.Add("TimeSpan", TimeSpan);
            templateVars.Add("Address", Address);
            templateVars.Add("ZipCode", ZipCode);
            templateVars.Add("City", City);
            templateVars.Add("CustomerApprovalLink", CustomerApprovalLink);
            templateVars.Add("CustomerDisapprovalLink", CustomerDisapprovalLink);
            Shared.Parser parser = new Shared.Parser(Server.MapPath("~/Reports/HtmlTemplates/CustomerEmail_ServiceSchedulePlan.htm"), templateVars);

            MembershipUser user = Membership.GetUser();

            email.SendEmail(CustomerEmail, ConfigurationManager.AppSettings["EmailAddress"].ToString(), parser.Parse(), Subject, true, test, user.Email, null, null, Convert.ToInt32(CustomerId), new Guid(user.ProviderUserKey.ToString()));
        }
    }

}